# ==============================================================================
#
# wwwldap.pm
#
# GSBWWW LDAP utility routines
#
# $Id: wwwldap.pm,v 1.6 2005/08/25 22:38:27 dyoung2 Exp $
#
# Darren Young [darren.young@gsb.uchicago.edu]
#
# ==============================================================================
#
# ChangeLog
#
# $Log: wwwldap.pm,v $
# Revision 1.6  2005/08/25 22:38:27  dyoung2
#   * Standardized BEGIN headers.
#
# Revision 1.5  2005/08/03 17:28:48  dyoung2
#   * Added _getWWWLdapAccountPassword().
#
# Revision 1.4  2005/06/27 16:31:02  dyoung2
#   * Merged in all the main functions.
#
# Revision 1.3  2005/06/14 17:52:05  dyoung2
#   * Working version for testing.
#
# Revision 1.2  2005/04/26 22:30:19  dyoung2
#   * Work in progress
#
# Revision 1.1  2005/04/13 22:43:50  dyoung2
#   * Work in progress
#
# ==============================================================================

my  $cvsid   = '$Id: wwwldap.pm,v 1.6 2005/08/25 22:38:27 dyoung2 Exp $';
my  @cvsinfo = split( ' ', $cvsid );
our $NAME    = File::Basename::basename($0);
our $VERSION = $cvsinfo[2];

# ------------------------------------------------------------------------------
#                             B E G I N
# ------------------------------------------------------------------------------
BEGIN {

    # Pragmas
    use strict;

    # "Standard" modules we use
    use FindBin;
    use Config::Simple;
    use lib "$FindBin::Bin/../lib";

    # Standard account maint modules to use
    use logging;
    use errcodes;
    use utils;
    
    # Modules that this module uses
    use Net::LDAP;
    use Lingua::EN::NameParse;
    use amtools;
    use dbtools;
    use unixtools;

}
 
# ------------------------------------------------------------------------------ 
#                             V A R I A B L E S
# ------------------------------------------------------------------------------ 
our $LOGFILE    = "$FindBin::Bin/../log/acctmgr.log" unless($LOGFILE); 
our $CONFIGFILE = "$FindBin::Bin/../etc/acctmgr.cfg" unless($CONFIGFILE); 


# ------------------------------------------------------------------------------
#                             C O N F I G   F I L E
# ------------------------------------------------------------------------------
our $CFG = new Config::Simple( filename => $CONFIGFILE ) or die "$!\n" unless($CFG);


# =============================================================================
# NAME        : _addWWWLdapAccount
# DESCRIPTION :
# REQUIRES    :
# RETURNS     :
# NOTES       :
# =============================================================================
sub _addWWWLdapAccount {
    my ( $login, $cryptPass ) = @_;
    my $name = "_addWWWLdapAccount";
    my ( $package, $filename, $line ) = caller;
    debug("$name: entering with args @_");
    debug( "$name: called from package->$package, filename->$filename, line->$line" );

    my $host     = $CFG->param("ldapwww.host");
    my $port     = $CFG->param("ldapwww.port");
    my $timeout  = $CFG->param("ldapwww.timeout");
    my $ldapver  = $CFG->param("ldapwww.ldapver");
    my $binddn   = $CFG->param("ldapwww.binduser");
    my $bindpw   = $CFG->param("ldapwww.bindpw");
    my $basedn   = $CFG->param("ldapwww.basedn");
    my $conn     = "$host" . ':' . "$port";
    my $ldap;


    ### attempt to connect to the LDAP service. if we connect, move on, otherwise
    ### we have to return a fatal error condidtion
    if ( $ldap  = Net::LDAP->new($conn, => timeout=>$timeout, version=>$ldapver)) {
        logmsg("$name: established LDAP connection to $conn");
    } else {
        logmsg("$name: FAILED to estblish LDAP connection to $conn");
        return(0);
    }

    ### attempt to bind to the LDAP directory
    if ( $mesg = $ldap->bind( $binddn, password => $bindpw ) ) {
        logmsg("$name: successful bind to $conn");
    } else {
        logmsg("$name: FAILED to bind to LDAP server $conn as $binddn");
        logmsg("$name: LDAP error code is " . $ldap->code);
        logmsg("$name: LDAP error text is " . $ldap->error);
        return(0);
    }


    my $firstname;
    my $lastname;
    my $fullname;
    my $mailAddress;
    my $userPassword;

    ### if we were called with a crypted password argument, make that the password
    ### if not, generate a random crypted password
    if ( ! $cryptPass ) {
        logmsg("$name: no password supplied, generating a random one");
        $userPassword = _makeRandomPassword();
    } else {
        logmsg("$name: password given to me, using the supplied one");
        $userPassword = $cryptPass;
    }

    local %outp;
    local %ldaptp;
    local %ldapgrp;
    
    if ( ! _setHashes() ) {
        logmsg("$name: FAILED to set configuration hashes");
        return(0);
    }

    ### grab the entire user record from the database
    my %dbrecord = getDBLoginRecord($login);

    my $class   = _getHighClass($dbrecord{'ACCT_CLASS'});
    debug("$name: got highest account class from db -> $class");


    ### create a new name parser object
    my $namething = new Lingua::EN::NameParse(
                                               auto_clean     => 1,
                                               force_case     => 1,
                                               lc_prefix      => 1,
                                               allow_reversed => 1
                                             );

    ### parse the fullname as it is in the database
    ### middle name is stored in the db along with the first name
    ### so we just concat those strings with a space in the middle 
    ### to build the user's full name
    my $error = $namething->parse($dbrecord{'FIRST_NAME'} . " " . $dbrecord{'LAST_NAME'});

    ### if the name parse worked, split up the portions correctly
    if ( ! $error ) {

        my %name   = $namething->case_components;
        $firstname = $name{'given_name_1'};
        $lastname  = $name{'surname_1'};

        if ( $name{'middle_name'} ) {
            $fullname  = $name{'given_name_1'} . " " . $name{'middle_name'} . " " .  $name{'surname_1'};
        } else {
            $fullname = $name{'given_name_1'} . " " . $name{'surname_1'};
        }

        debug("$name: firstname after parse -> $firstname");
        debug("$name:  lastname after parse -> $lastname");
        debug("$name:  fullname after parse -> $fullname");

    } else {
        logmsg("$name: FAILED to parse fullname for $login");
        logmsg("$name: name parse error -> $error");
        return(0);
    }


    ### build the dn based on what we've gathered so far
    my $dn = "uid=$login" . ',ou=' . "$outp{$class}" . ",$basedn";
    debug("$name: dn to add -> $dn");

    ### if it's a student set the mail to username@
    ### otherwise, it's first.last@
    if ( $class eq "m" || $class eq "n" || $class eq "a" || $class =~ /^t/ ) {
        $mailAddress = $login . '@gsb.uchicago.edu';
    } else {
        $mailAddress    = "$fna" . '.' . "$lna" . '@gsb.uchicago.edu';
    }


    logmsg("$name: adding dn with the following attributes");
    logmsg("$name:           cn -> $fullname");
    logmsg("$name:           sn -> $lastname");
    logmsg("$name:         mail -> $mailAddress");
    logmsg("$name:          uid -> $login");
    logmsg("$name:           ou -> $outp{$class}");
    logmsg("$name:   typePerson -> $ldaptp{$class}");
    logmsg("$name: userPassword -> {crypt}$userPassword");


    ### add the account to WWW LDAP
    my $result = $ldap->add(
        "$dn",
            attr => [
                'cn'                   => "$fullname",
                'sn'                   => "$lastname",
                'mail'                 => "$mailAddress",
                'uid'                  => "$login",
                'OU'                   => "$outp{$class}",
                'typePerson'           => "$ldaptp{$class}",
                'userPassword'         => '{crypt}' . "$userPassword",
                'objectclass' => [ 'top', 'person', 'organizationalPerson', 'inetOrgPerson' ],
            ]
        );

    if ( $result->code ) {
        logmsg("$name: FAILED to add entry DN: $dn");
        logmsg("$name: LDAP error code -> " . $result->code);
        logmsg("$name: LDAP error text -> " . $result->error);
        return(0);
    } else {
        logmsg("$name: SUCCESSFULLY added LDAP entry DN: $dn");
        $retval = 1;
    }

    ### if the user is supposed to be in an ldap group, add them
    ### otherwise, simply log what we didn't do
    if ( $ldapgrp{$class} ) {
        my $dngrp = 'cn=' . "$ldapgrp{$class}" . ",$basedn";
        logmsg( "$name: entry needs to be in an LDAP group for " . $ldapgrp{$class} );
        logmsg("$name: LDAP group should be $dngrp");
        $result = $ldap->modify( $dngrp, add => { 'uniquemember' => "$dn" } );

        if ( $result->code ) {
            logmsg("$name: failed to add member to LDAP group");
            logmsg( "$name: LDAP error code is: " . $result->code );
            logmsg( "$name: LDAP error text is: " . $result->text );
            return(0);
        } else {
            logmsg("$name: SUCCESSFULLY added DN -> $dn to LDAP group -> $dngrp");
            $retval = 1;
        }
    } else {
        logmsg("$name: WARNING: no LDAP group for class $class, only the dn object was added");
        $retval = 1;
    }

    if ($ldap) {
        $ldap->unbind;
    }

    logmsg("$name: returning with value of $retval");
    return($retval);
}


# =============================================================================
# NAME        : _validWWWLdapAccount
# DESCRIPTION : checks LDAP on GSBWWW to see if a user exists or not
# REQUIRES    : string(login)
# RETURNS     : TRUE or FALSE
# NOTES       :
# =============================================================================
sub _isValidWWWLdapAccount {
    my ($uname) = @_;
    my $name = "_validWWWLdapAccount";
    my ( $package, $filename, $line ) = caller;
    debug("$name: entering with args @_");
    debug("$name: called from package->$package, filename->$filename, line->$line");

    my $host     = $CFG->param("ldapwww.host");
    my $port     = $CFG->param("ldapwww.port");
    my $timeout  = $CFG->param("ldapwww.timeout");
    my $ldapver  = $CFG->param("ldapwww.ldapver");
    my $binddn   = $CFG->param("ldapwww.binduser");
    my $bindpw   = $CFG->param("ldapwww.bindpw");
    my $basedn   = $CFG->param("ldapwww.basedn");
    my $conn     = "$host" . ':' . "$port";
    my $retval   = 0;
    my $ldap     = "";

    # attempt to connect to the LDAP service. if we connect, move on, otherwise
    # we have to return a fatal error condidtion
    if ( $ldap  = Net::LDAP->new($conn, => timeout=>$timeout, version=>$ldapver)) {
        debug("$name: established LDAP connection to $conn");
    } else {
        logmsg("$name: FAILED to estblish LDAP connection to $conn");
        return(0);
    }

    if ( $ldap->bind( $binddn, password => $bindpw ) ) {
        debug("$name: successful bind to $conn");
    } else {
        logmsg("$name: FAILED to bind to LDAP server $conn");
        logmsg("$name: LDAP error code is " . $ldap->code);
        logmsg("$name: LDAP error text is " . $ldap->error);
        return(0);
    }

    # start this search at the top of the tree since they could be in any number
    # of ou's
    debug("$name: searching for $uname in $basedn");
    my $mesg = $ldap->search( base   => "$basedn",
                              filter => '(&(uid=' . $uname . '))' );

    if ( $mesg->code ) {
        logmsg("$name: LDAP search FAILED");
        logmsg("$name: LDAP error code is " . $mesg->code);
        logmsg("$name: LDAP error text is: " . $mesg->error );
        $retval = 0;
    } else {
        if ( $mesg->count == 0 ) {
            debug("$name: no entry for $uname in $basedn");
            $retval = 0;
        } elsif ( $mesg->count == 1 ) {
            debug("$name: found 1 entry for $uname in $basedn");
            $retval = 1;
        } elsif ( $mesg->count > 1 ) {
            logmsg( "$name: *** WARNING *** found more than 1 entry for $uname in $basedn" );
            $retval = 1;    ## set to true since we did find at least one
        }
    }

    if ($ldap) {
        $ldap->unbind;
    }

    debug("$name: returning with val $retval");
    return($retval);
}

# =============================================================================
# NAME        : _getWWWLdapAccountOU
# DESCRIPTION : Return the ou for a given account
# REQUIRES    : string(login)
# RETURNS     : true or false
# NOTES       : none
# =============================================================================
sub _getWWWLdapAccountOU {
    my ($uname) = @_;
    my $name = "_getWWWLdapAccountOU";
    my ( $package, $filename, $line ) = caller;
    debug("$name: entering with args @_");
    debug("$name: called from package->$package, filename->$filename, line->$line");

    my $host     = $CFG->param("ldapwww.host");
    my $port     = $CFG->param("ldapwww.port");
    my $timeout  = $CFG->param("ldapwww.timeout");
    my $ldapver  = $CFG->param("ldapwww.ldapver");
    my $binddn   = $CFG->param("ldapwww.binduser");
    my $bindpw   = $CFG->param("ldapwww.bindpw");
    my $basedn   = $CFG->param("ldapwww.basedn");
    my $conn     = "$host" . ':' . "$port";
    my $retval   = 1;
    my $ldap     = "";

    # attempt to connect to the LDAP service. if we connect, move on, otherwise
    # we have to return a fatal error condidtion
    if ( $ldap  = Net::LDAP->new($conn, => timeout=>$timeout, version=>$ldapver)) {
        debug("$name: established LDAP connection to $conn");
    } else {
        logmsg("$name: FAILED to estblish LDAP connection to $conn");
        return(0);
    }

    if ( $ldap->bind( $binddn, password => $bindpw ) ) {
        debug("$name: successful bind to $conn");
    } else {
        logmsg("$name: FAILED to bind to LDAP server $conn");
        logmsg("$name: LDAP error code is " . $ldap->code);
        logmsg("$name: LDAP error text is " . $ldap->error);
        return(0);
    }

    # Start this search from the top level of the LDAP tree
    $mesg = $ldap->search( base   => "$basedn",
                           filter => '(&(uid=' . $uname . '))',
                           attrs  => 'ou'
                           );

    # If there's a message code, print the error and set to bad
    # Otherwise, move on.
    # We should get only 1 entry on the search, if it's 0 it's bad, if it's more
    # than 1 it's bad.
    if ( $mesg->code ) {
        logmsg("$name: LDAP search FAILED");
        logmsg("$name: LDAP error code is " . $mesg->code);
        logmsg("$name: LDAP error text is: " . $mesg->error );
        $retval = 0;
    } else {
        if ( $mesg->count > 0 ) {
            if ( $mesg->count > 1 ) {
                logmsg( "$name: found more than 1 LDAP entry for $uname (that's bad)" );
                $retval = 0;
            } else {
                debug( "$name: found " . $mesg->count . " entries" );
                my $entry = $mesg->entry(0);
                my $ou    = $entry->get_value("ou");
                $retval = qq($ou);
            }
        } else {
            logmsg("$name: FAILED to find an LDAP entry for $uname");
            $retval = 0;
        }
    }

    if ($ldap) {
        $ldap->unbind;
    }

    debug("$name: returning ou=$retval");
    return ($retval);

}

# =============================================================================
# NAME        : _deleteWWWLdapAccount
# DESCRIPTION : Deletes a given WWW LDAP account
# REQUIRES    : string(login)
# RETURNS     : true or false
# NOTES       : none
# =============================================================================
sub _deleteWWWLdapAccount {
    my ( $login ) = @_;
    my $name = "_deleteWWWLdapAccount";
    my ( $package, $filename, $line ) = caller;
    logmsg("$name: entering with args @_");
    logmsg("$name: called from package->$package, filename->$filename, line->$line");

    my $host     = $CFG->param("ldapwww.host");
    my $port     = $CFG->param("ldapwww.port");
    my $timeout  = $CFG->param("ldapwww.timeout");
    my $ldapver  = $CFG->param("ldapwww.ldapver");
    my $binddn   = $CFG->param("ldapwww.binduser");
    my $bindpw   = $CFG->param("ldapwww.bindpw");
    my $basedn   = $CFG->param("ldapwww.basedn");
    my $conn     = "$host" . ':' . "$port";
    my $retval   = 1;
    my $ldap     = "";
    my $ou       = "";
    my $dn       = "";


    # attempt to connect to the LDAP service. if we connect, move on, otherwise
    # we have to return a fatal error condidtion
    if ( $ldap  = Net::LDAP->new($conn, => timeout=>$timeout, version=>$ldapver)) {
        logmsg("$name: established LDAP connection to $conn");
    } else {
        logmsg("$name: FAILED to estblish LDAP connection to $conn");
        return(0);
    }

    if ( $mesg = $ldap->bind( $binddn, password => $bindpw ) ) {
        logmsg("$name: successful bind to $conn");
    } else {
        logmsg("$name: FAILED to bind to LDAP server $conn as $binddn");
        logmsg("$name: LDAP error code is " . $ldap->code);
        logmsg("$name: LDAP error text is " . $ldap->error);
        return(0);
    }

    if ( $mesg->code ) {
        logmsg("$name: FAILED to bind to $conn");
        logmsg("$name: LDAP error is: " . $mesg->error);
        return(0);
    } else {
        $ou = _getWWWLdapAccountOU($login);
        logmsg("$name: OU => $ou");

        $dn = "uid=$login" . ',ou=' . $ou . "," . $basedn;
        logmsg("$name: dn to delete->$dn");

        my $result = $ldap->delete($dn);    ## delete whole entry ##
        if ( $result->code ) {
            logmsg("$name: FAILED to delete entry $dn");
            logmsg("$name: LDAP error is: " . $result->error);
            $retval = 0;
        } else {
            logmsg("$name: deleted entry $dn");
        }
    }

    if ( $ldap ) {
        $ldap->unbind;
    }

    debug("$name: returning with val $retval");
    return($retval);
}


# =============================================================================
# NAME        : 
# DESCRIPTION : 
# REQUIRES    : 
# RETURNS     : 
# NOTES       : 
# =============================================================================
sub _getWWWLdapAccountDN {
    my ($uname) = @_;
    my $name = "_getWWWLdapAccountDN";
    my ( $package, $filename, $line ) = caller;
    debug("$name: entering with args @_");
    debug("$name: called from package->$package, filename->$filename, line->$line");

    my $host     = $CFG->param("ldapwww.host");
    my $port     = $CFG->param("ldapwww.port");
    my $timeout  = $CFG->param("ldapwww.timeout");
    my $ldapver  = $CFG->param("ldapwww.ldapver");
    my $binddn   = $CFG->param("ldapwww.binduser");
    my $bindpw   = $CFG->param("ldapwww.bindpw");
    my $basedn   = $CFG->param("ldapwww.basedn");
    my $conn     = "$host" . ':' . "$port";
    my $retval   = 1;
    my $ldap     = "";

    # attempt to connect to the LDAP service. if we connect, move on, otherwise
    # we have to return a fatal error condidtion
    if ( $ldap  = Net::LDAP->new($conn, => timeout=>$timeout, version=>$ldapver)) {
        debug("$name: established LDAP connection to $conn");
    } else {
        logmsg("$name: FAILED to estblish LDAP connection to $conn");
        return(0);
    }

    if ( $ldap->bind( $binddn, password => $bindpw ) ) {
        debug("$name: successful bind to $conn");
    } else {
        logmsg("$name: FAILED to bind to LDAP server $conn");
        logmsg("$name: LDAP error code is " . $ldap->code);
        logmsg("$name: LDAP error text is " . $ldap->error);
        return(0);
    }

    # Start this search from the top level of the LDAP tree
    $mesg = $ldap->search( base   => "$basedn",
                           filter => '(uid=' . $uname . ')'
                           );

    # If there's a message code, print the error and set to bad
    # Otherwise, move on.
    # We should get only 1 entry on the search, if it's 0 it's bad, if it's more
    # than 1 it's bad.
    if ( $mesg->code ) {
        logmsg("$name: LDAP search FAILED");
        logmsg("$name: LDAP error code is " . $mesg->code);
        logmsg("$name: LDAP error text is: " . $mesg->error );
        $retval = 0;
    } else {
        if ( $mesg->count > 0 ) {
            if ( $mesg->count > 1 ) {
                logmsg( "$name: found more than 1 LDAP entry for $uname (that's bad)" );
                $retval = 0;
            } else {
                debug( "$name: found " . $mesg->count . " entries" );
                my $entry = $mesg->entry(0);
                my $dn    = $entry->dn();
                $retval = qq($dn);
            }
        } else {
            logmsg("$name: FAILED to find an LDAP entry for $uname");
            $retval = 0;
        }
    }

    if ($ldap) {
        $ldap->unbind;
    }

    debug("$name: returning dn=$retval");
    return ($retval);

}


# =============================================================================
# NAME        : 
# DESCRIPTION : 
# REQUIRES    : 
# RETURNS     : 
# NOTES       : 
# =============================================================================
sub _moveWWWLdapAccountOU {
    my ($uname, $destou) = @_;
    my $name = "_moveWWWLdapAccountOU";
    my ( $package, $filename, $line ) = caller;
    debug("$name: entering with args @_");
    debug("$name: called from package->$package, filename->$filename, line->$line");

    my $host     = $CFG->param("ldapwww.host");
    my $port     = $CFG->param("ldapwww.port");
    my $timeout  = $CFG->param("ldapwww.timeout");
    my $ldapver  = $CFG->param("ldapwww.ldapver");
    my $binddn   = $CFG->param("ldapwww.binduser");
    my $bindpw   = $CFG->param("ldapwww.bindpw");
    my $basedn   = $CFG->param("ldapwww.basedn");
    my $conn     = "$host" . ':' . "$port";
    my $retval   = 1;
    my $ldap     = "";

    # attempt to connect to the LDAP service. if we connect, move on, otherwise
    # we have to return a fatal error condidtion
    if ( $ldap  = Net::LDAP->new($conn, => timeout=>$timeout, version=>$ldapver, debug=>0)) {
        debug("$name: established LDAP connection to $conn");
    } else {
        logmsg("$name: FAILED to estblish LDAP connection to $conn");
        return(0);
    }

    if ( $ldap->bind( $binddn, password => $bindpw ) ) {
        debug("$name: successful bind to $conn");
    } else {
        logmsg("$name: FAILED to bind to LDAP server $conn");
        logmsg("$name: LDAP error code is " . $ldap->code);
        logmsg("$name: LDAP error text is " . $ldap->error);
        return(0);
    }

    # Start this search from the top level of the LDAP tree
    $mesg = $ldap->search( base   => "$basedn",
                           filter => '(uid=' . $uname . ')'
                           );

    # If there's a message code, print the error and set to bad
    # Otherwise, move on.
    # We should get only 1 entry on the search, if it's 0 it's bad, if it's more
    # than 1 it's bad.
    if ( $mesg->code ) {
        logmsg("$name: LDAP search FAILED");
        logmsg("$name: LDAP error code is " . $mesg->code);
        logmsg("$name: LDAP error text is: " . $mesg->error );
        $retval = 0;
    } else {
        if ( $mesg->count > 0 ) {
            if ( $mesg->count > 1 ) {
                logmsg( "$name: found more than 1 LDAP entry for $uname (that's bad)" );
                $retval = 0;
            } else {
                debug( "$name: found " . $mesg->count . " entries" );
                my $entry = $mesg->entry(0);
                my $dn    = $entry->dn();
                my $rdn   = "uid=$uname";
                debug("$name:     dn -> $dn");
                debug("$name:    rdn -> $rdn");
                debug("$name: destou -> $destou");

                $mesg = $ldap->moddn(
                               dn          => $dn,
                               newrdn      => $rdn,
                               newsuperior => $destou );

                if ( $mesg->code ) {
                    logmsg("$name: FAILED to move user -> $uname");
                    logmsg("$name: LDAP error code is " . $mesg->code);
                    logmsg("$name: LDAP error text is: " . $mesg->error );
                    $retval = 0;
                } else {
                    logmsg("$name: moved $uname to $destou");
                    $retval = 1;
                }
            }

        } else {
            logmsg("$name: FAILED to find an LDAP entry for $uname");
            $retval = 0;
        }
    }

    if ($ldap) {
        $ldap->unbind;
    }

    debug("$name: returning with val $retval");
    return ($retval);

}


# =============================================================================
# NAME        : _getWWWLdapAccountPassword
# DESCRIPTION : Return the userPassword for a given account
# REQUIRES    : string(login)
# RETURNS     : true or false
# NOTES       : none
# =============================================================================
sub _getWWWLdapAccountPassword {
    my ($uname) = @_;
    my $name = "_getWWWLdapAccountPassword";
    my ( $package, $filename, $line ) = caller;
    debug("$name: entering with args @_");
    debug("$name: called from package->$package, filename->$filename, line->$line");

    my $host     = $CFG->param("ldapwww.host");
    my $port     = $CFG->param("ldapwww.port");
    my $timeout  = $CFG->param("ldapwww.timeout");
    my $ldapver  = $CFG->param("ldapwww.ldapver");
    my $binddn   = $CFG->param("ldapwww.binduser");
    my $bindpw   = $CFG->param("ldapwww.bindpw");
    my $basedn   = $CFG->param("ldapwww.basedn");
    my $conn     = "$host" . ':' . "$port";
    my $retval   = 1;
    my $ldap     = "";

    # attempt to connect to the LDAP service. if we connect, move on, otherwise
    # we have to return a fatal error condidtion
    if ( $ldap  = Net::LDAP->new($conn, => timeout=>$timeout, version=>$ldapver)) {
        debug("$name: established LDAP connection to $conn");
    } else {
        logmsg("$name: FAILED to estblish LDAP connection to $conn");
        return(0);
    }

    if ( $ldap->bind( $binddn, password => $bindpw ) ) {
        debug("$name: successful bind to $conn");
    } else {
        logmsg("$name: FAILED to bind to LDAP server $conn");
        logmsg("$name: LDAP error code is " . $ldap->code);
        logmsg("$name: LDAP error text is " . $ldap->error);
        return(0);
    }

    # Start this search from the top level of the LDAP tree
    $mesg = $ldap->search( base   => "$basedn",
                           filter => '(&(uid=' . $uname . '))',
                           attrs  => 'userPassword'
                           );

    # If there's a message code, print the error and set to bad
    # Otherwise, move on.
    # We should get only 1 entry on the search, if it's 0 it's bad, if it's more
    # than 1 it's bad.
    if ( $mesg->code ) {
        logmsg("$name: LDAP search FAILED");
        logmsg("$name: LDAP error code is " . $mesg->code);
        logmsg("$name: LDAP error text is: " . $mesg->error );
        $retval = 0;
    } else {
        if ( $mesg->count > 0 ) {
            if ( $mesg->count > 1 ) {
                logmsg( "$name: found more than 1 LDAP entry for $uname (that's bad)" );
                $retval = 0;
            } else {
                debug( "$name: found " . $mesg->count . " entries" );
                my $entry = $mesg->entry(0);
                my $cpw   = $entry->get_value("userPassword");
                if ( $cpw eq "" ) {
                    logmsg("$name: userPassword for $uname is not set");
                    $retval = 0;
                } else {
                    my ($ctype, $pw) = split('}', $cpw);
                    my $type = substr($ctype, 1);
                    $retval = qq($pw);
                }
            }
        } else {
            logmsg("$name: FAILED to find an LDAP entry for $uname");
            $retval = 0;
        }
    }

    if ($ldap) {
        $ldap->unbind;
    }

    debug("$name: returning pw=$retval");
    return ($retval);

}


# =============================================================================
# NAME        : _setWWWLdapPassword
# DESCRIPTION : change a user's password in LDAP on GSBWWW
# REQUIRES    : string(uname), string(password), string(ou)
# RETURNS     : TRUE or FALSE
# NOTES       : $password is a UNIX crypt() password
# =============================================================================
sub _setWWWLdapPassword {
    my ( $uname, $password, $ou ) = @_;
    my $name = "_setWWWLdapPassword";
    my ( $package, $filename, $line ) = caller;
    logmsg("$name: entering with args @_");
    logmsg("$name: called from package->$package, filename->$filename, line->$line");

    my $host   = $CFG->param("ldapwww.host");
    my $port   = $CFG->param("ldapwww.port");
    my $dmb    = $CFG->param("ldapwww.binduser");
    my $dmPass = $CFG->param("ldapwww.bindpw");

    my $connstring = "$host" . ':' . "$port";
    my $dnb        = 'cn=Directory Manager';

    my $ldap = Net::LDAP->new($connstring)
      or logmsg("$name: FAILED to bind to ldap server $connstring");

    $ldap->bind( $dnb, password => "$dmPass" );
    if ($ldap) {
        logmsg("$name: successful bind");
    }

    my $retval = 1;
    if ($ou) {
        my $dn = "uid=$uname" . ',ou=' . $ou . ',o=University of Chicago,c=US';

        my $result =
          $ldap->modify( $dn, replace => { 'userPassword' => '{crypt}' . "$password" } );    ##

        if ( $result->code ) {
            logmsg( "$name: FAILED to change password for $dn, error is: " . $result->error() );
            $retval = 0;
        } else {
            logmsg("$name: changed password on $host:$port for dn: $dn");
        }

    } else {
        logmsg("$name: FAILED to change password, no ou given to me!");
    }
    return $retval;
}

1;
